home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-20
/
pmpsrc11.zip
/
PMP.C
< prev
next >
Wrap
Text File
|
1991-07-30
|
20KB
|
839 lines
/*
pmp.c -- main program
Poor Man's Packet (PMP)
Copyright (c) 1991 by Andrew C. Payne All Rights Reserved.
Permission to use, copy, modify, and distribute this software and its
documentation without fee for NON-COMMERCIAL AMATEUR RADIO USE ONLY is hereby
granted, provided that the above copyright notice appear in all copies.
The author makes no representations about the suitability of this software
for any purpose. It is provided "as is" without express or implied warranty.
July, 1989
Andrew C. Payne
*/
/* ----- Includes ----- */
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <alloc.h>
#include <mem.h>
#include <bios.h>
#include <dos.h>
#include <io.h>
#include <ctype.h>
#include <time.h>
#include <string.h>
#define EXTERN
#include "keys.h"
#include "pmp.h"
#include "ports.h"
int holdmode;
/* ----- Title Screen ----- */
/* TitleScreen()
Shows the title screen.
*/
static void TitleScreen(void)
{
clrscr();
normal();
cprintf(" ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄\r\n");
cprintf(" ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄\r\n");
cprintf(" ▄▄▄▄▄▄▄ ▓▓▓▓▓▓▓▓ ▓▓ ▓▓ ▓▓▓▓▓▓▓▓ ▄▄▄▄▄▄▄\r\n");
cprintf(" ▄▄▄▄▄▄▄ ▓▓ ▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓ ▓▓ ▄▄▄▄▄▄▄\r\n");
cprintf(" ▄▄▄▄▄▄▄ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▄▄▄▄▄▄▄\r\n");
cprintf(" ▄▄▄▄▄▄▄ ▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓ ▓▓ ▓▓▓▓▓▓▓▓ ▄▄▄▄▄▄▄\r\n");
cprintf(" ▄▄▄▄▄▄▄ ▓▓ ▓▓ ▓▓ ▓▓ ▄▄▄▄▄▄▄\r\n");
cprintf(" ▄▄▄▄▄▄▄ ▓▓ ▓▓ ▓▓ ▓▓ ▄▄▄▄▄▄▄\r\n");
cprintf(" ▄▄▄▄▄▄▄ ▓▓ oor ▓▓ ▓▓ an's ▓▓ acket ▄▄▄▄▄▄▄\r\n");
cprintf(" ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄\r\n\n");
bright();
cprintf(" A Complete Packet Radio Program For The IBM PC\r\n\n");
normal();
cprintf(" Version %s of %s \r\n\n\n",VERSION,__DATE__);
cprintf(" By Andrew C. Payne, N8KEI\r\n");
cprintf(" with special thanks to Kevin Feeney, WB2EMS\r\n\n");
cprintf(" Copyright (c) 1989, 1990, 1991 ACP\r\n");
cprintf(" All rights reserved.\r\n");
}
/* ----- Subroutines ----- */
/* stoupper(s)
Convert string to upper case.
*/
void stoupper(char *s)
{
while(*s) {
*s = toupper(*s);
s++;
}
}
/* wait()
Prompts user for keystroke.
*/
void wait(void)
{
bright();
gotoxy(1,25);
cprintf(" ----- Press any key to continue ------");
(void)getkey();
}
/* WaitKey(p)
Waits for a keystroke, calling PeriodicHook() while waiting.
Returns keystroke. If 'p' is non-zero, then a prompt is shown
on line 'p'.
*/
KEY WaitKey(int prompt)
{
if(prompt)
CenterTitle(prompt," Press any key to resume ");
while(!keypressed())
PeriodicHook();
return getkey();
}
/* ----- Sound Control ----- */
/* StartSound(freq,time)
Starts a sound of the given frequency and duration (in 50 ms BIOS
clock ticks).
*/
void StartSound(int freq, int time)
{
if(Sound) {
sound(freq);
SoundEnd = BiosTime() + time;
}
}
/* ----- Upper Level User Commands ------ */
/* DoHelp()
Handles the help screen.
*/
void DoHelp(void)
{
SaveScreen(TRUE,TRUE);
CenterTitle(1," PMP Help ");
GotoXY(1,2);
_uputs(NormalAttr,"\n");
_uputs(NormalAttr,"│ ALT-C Connect F1-F6 User defined macros\n");
_uputs(NormalAttr,"│ ALT-B Send Beacon\n");
_uputs(NormalAttr,"│ ALT-D Disconnect\n");
_uputs(NormalAttr,"│ ALT-H Show help (this screen) UP,DOWN Scrollback line at a time\n");
_uputs(NormalAttr,"│ ALT-J Screen shapshot to file PGUP/DN Scrollback page at a time\n");
_uputs(NormalAttr,"│ ALT-L Download/capture a text file\n");
_uputs(NormalAttr,"│ ALT-N Nodes recently heard\n");
_uputs(NormalAttr,"│ ALT-P Pause screen\n");
_uputs(NormalAttr,"│ ALT-S Show system status\n");
_uputs(NormalAttr,"│ ALT-U Upload a text file\n");
_uputs(NormalAttr,"│ ALT-W Write scrollback to disk\n");
_uputs(NormalAttr,"│ ALT-X Exit PMP\n\n");
_uputs(NormalAttr,"│ ────────────────────────────────────────────────────────────────────────\n");
_uputs(NormalAttr,"│ Comments, questions, and money to:\n\n");
_uputs(NormalAttr,"│ Andrew C. Payne\n");
_uputs(NormalAttr,"│ Route 3, Box 78-Q\n");
_uputs(NormalAttr,"│ Berkeley Springs, WV 25411");
(void)WaitKey(23);
RestoreScreen();
}
/* DoDebug()
Toggle debug mode on and off.
*/
void DoDebug(void)
{
DebugMode = !DebugMode;
putstring(1,25,1,InvAttr, DebugMode ? "■" : " ");
}
/* DoConnect()
Prompts user for connect path, starts connection.
*/
static void DoConnect(void)
{
char path[80];
/* if we are disconnected, prompt for connect path and initiate connect */
if(AX25_Control.state == DISCONNECTED) {
if(GetInput("Connect to --> ",path,60)) {
memcpy(&AX25_Control.header.source,
&MyCall,sizeof(struct ax25_addr));
AX25_Control.header.pid = PID_TEXT;
AX25_Control.type = TEXT;
if(SetAX25Path(path,&AX25_Control.header))
AX25_Open();
}
}
}
/* DoStatus()
Shows the status of the node, primarily the health counters.
*/
static void DoStatus(void)
{
char s[80];
long t;
struct tm *tm;
int hours,minutes,secs;
#ifdef TRACE
extern int nlogs;
#endif
SaveScreen(TRUE,TRUE);
CenterTitle(1," PMP Status ");
GotoXY(1,4);
time(&t);
tm = localtime(&t);
sprintf(s,"│ Status at %02d/%02d/%02d %02d:%02d:%02d ",
tm->tm_mon+1, tm->tm_mday, tm->tm_year, tm->tm_hour,
tm->tm_min, tm->tm_sec);
_uputs(NormalAttr,s);
t -= StartTime;
secs = t % 60;
t /= 60;
minutes = t % 60;
t /= 60;
hours = t % 24;
t /= 24;
sprintf(s," Uptime %d %02d:%02d:%02d\n\n",
(int)t, hours, minutes, secs);
_uputs(NormalAttr,s);
sprintf(s,"│ %8ld good frames received\n",RXCount);
_uputs(NormalAttr,s);
sprintf(s,"│ %8ld framing errors\n",RXFrameErr);
_uputs(NormalAttr,s);
sprintf(s,"│ %8ld checksum errors\n\n",RXCRCErr);
_uputs(NormalAttr,s);
sprintf(s,"│ %8ld receive queue overflows\n",RXQOverflow);
_uputs(NormalAttr,s);
sprintf(s,"│ %8ld receive buffer overflows\n",RXBOverflow);
_uputs(NormalAttr,s);
sprintf(s,"│ %8ld REJ frames received\n",RXREJ);
_uputs(NormalAttr,s);
sprintf(s,"│ %8ld FRMR frames received\n\n",RXFRMR);
_uputs(NormalAttr,s);
sprintf(s,"│ %8ld frames transmitted\n\n",TXCount);
_uputs(NormalAttr,s);
#ifdef TRACE
sprintf(s,"│ %8ld frames in trace log\n\n",(long)nlogs);
_uputs(NormalAttr,s);
#endif
sprintf(s,"│ %12ld bytes free\n\n",coreleft());
_uputs(NormalAttr,s);
if(Capturing()) {
sprintf(s,"│ Capturing to %s (%ld bytes captured)",
CaptureFile, CaptureSize);
_uputs(NormalAttr,s);
}
(void)WaitKey(23);
RestoreScreen();
}
/* DoPause()
Pause the screen.
*/
void DoPause(void)
{
SaveScreen(TRUE,FALSE);
(void)WaitKey(23);
RestoreScreen();
}
/* DoUpload()
Uploads a file.
Crude first version, error handling sucks.
*/
void DoUpload(void)
{
char fname[40]; /* file name to upload */
char inbuf[256]; /* input buffer */
char text[90]; /* temp text area */
long filesize; /* length of file in bytes */
long starttime; /* start time of upload */
int uploadtime; /* time used to upload */
FILE *upfile;
int cursor;
int len; /* # of bytes acutally read */
/* can't upload unless connected */
if(!Connected()) {
sound(400);
delay(200);
nosound();
return;
}
/* get filename, open file */
if(GetInput("Upload what file? --> ",fname,40)) {
if(!*fname)
return;
stoupper(fname);
upfile = fopen(fname,"r");
if(upfile == NULL) { /* error on open */
uprintf(InvAttr," Error opening '%s' : %s \n",fname, sys_errlist[errno]);
return;
}
/* show upload status */
filesize = filelength(fileno(upfile));
cursor = cursave();
curoff();
sprintf(text," Uploading %-40s Press <ESC> to abort",fname);
putstring(1,24,80,InvAttr,text);
starttime = BiosTime();
/* read lines from file */
while(len = fread(inbuf, 1, 256, upfile)) {
/* wait for space in AX.25 queue, then enqueue the data */
do {
PeriodicHook(); /* wait to empty */
if(keypressed() && (getkey() == ESC)) {
Notify(" Upload aborted.");
goto done;
}
} while(AX25QFull());
LinkSend((byte *)inbuf, len); /* send the data */
uputtext(BrightAttr, inbuf, len);
}
uploadtime = (BiosTime() - starttime) / BIOSSEC;
if(uploadtime == 0)
uploadtime = 1;
/* if terminated for any reason other than EOF, handle error */
if(ferror(upfile)) {
uprintf(InvAttr," Error reading '%s' : %s \n",
fname, sys_errlist[errno]);
} else {
if(Sound) { /* two beeps */
sound(2600);
delay(100);
nosound();
delay(50);
sound(2600);
delay(100);
nosound();
}
sprintf(text," Upload complete. (%d seconds for %ld bytes = %ld bytes/sec)",
uploadtime, filesize, (long)(filesize/uploadtime));
Notify(text);
}
done:
clear_area(24,1,80); /* clean up */
currest(cursor);
fclose(upfile);
}
}
/* DoCapture()
Toggles file capture (log to disk).
Note: The test for a file aready existing could be cleaned up
using 'access'.
*/
void DoCapture(void)
{
char fname[41]; /* capture filename */
char text[80]; /* temp text area */
FILE *test;
/* If already capturing, close file */
if(Capturing()) {
sprintf(text," Closing capture file %s (%ld bytes)",
CaptureFile, CaptureSize);
Notify(text);
CloseCapture();
putstring(60,25,3,StatusAttr,"");
return;
}
/* get filename */
if(GetInput("Capture file? --> ",fname,40)) {
if(!*fname)
return;
if((test = fopen(fname,"r")) != NULL) {
fclose(test);
if(!GetInput("File already exists. Overwrite? [Y/N]",text,2))
return;
if(toupper(*text) != 'Y')
return;
}
stoupper(fname);
OpenCapture(fname);
putstring(60,25,3,StatusAttr,"Cap");
}
}
/* DoSnapshot()
Write a snapshot of the current screen to a file.
*/
void DoSnapshot(void)
{
char fname[41]; /* capture snapshot */
FILE *test;
char temp[160]; /* temp area */
char line[81]; /* output line */
int i,l; /* screen line */
char *p,*q;
/* open snapshot file*/
if(!GetInput("Snapshot to what file? --> ",fname,40))
return;
if(!*fname)
return;
if((test = fopen(fname,"r")) != NULL) {
fclose(test);
if(!GetInput("File already exists. Overwrite? [Y/N]",line,2))
return;
if(toupper(*line) != 'Y')
return;
}
if((test = fopen(fname,"w")) == NULL) {
Notify(" Can't open snapshot file.");
return;
}
/* write contents of screen to file */
for(l=1; l<24; l++) {
gettext(1,l,80,l,temp);
q = line;
p = temp;
for(i=0; i<80; i++) { /* extract contents */
*q++ = *p++;
p++;
}
do { /* trim line */
q--;
} while(q >= line && (*q == '\0' || *q == ' '));
*++q = '\n';
*++q = '\0';
fputs(line, test); /* should error check this */
}
fclose(test);
}
/* DoWrite()
Writes the contents of the scrollback buffer to the file specified.
*/
void DoWrite(void)
{
char fname[41]; /* capture snapshot */
FILE *test;
char line[81]; /* output line */
/* open snapshot file*/
if(!GetInput("Write scrollback buffer to what file? --> ",fname,40))
return;
if(!*fname)
return;
if((test = fopen(fname,"r")) != NULL) {
fclose(test);
if(!GetInput("File already exists. Overwrite? [Y/N]",line,2))
return;
if(toupper(*line) != 'Y')
return;
}
WriteScrollback(fname);
}
/* ----- Main Loop ----- */
/* PeriodicHook()
When in an idle loop, this routine should be called as much as
possible. If the carrier is down, goes to RX, handles timers
and flushes the TX queue.
*/
void PeriodicHook(void)
{
long t;
/* Go to RX if a carrier is detected */
while(RXCarrier()) {
if(RXLevel1()) /* receive incoming */
RXProcess(); /* process incoming */
Pwait = 0;
}
/* process AX.25 link transmit items, if any */
AX25_Flush();
/* check for expired timers */
t = BiosTime();
if(AX25_Control.t1 != 0 && AX25_Control.t1 <= t)
AX25_Expire(1);
if(AX25_Control.t3 != 0 && AX25_Control.t3 <= t)
AX25_Expire(3);
if(SoundEnd && t >= SoundEnd) { /* sound timeout */
nosound();
SoundEnd = 0;
}
if(BeaconEnd && t >= BeaconEnd) { /* beacon timeout */
SendBeacon();
StartBeacon();
}
/* do p-persistence, and flush anything in the TX queue */
if(Pwait == 0 || t > Pwait) { /* attempt transmission */
if(rand() < Ppersist) { /* we are a go? */
Pwait = 0;
TXQEmpty(); /* Transmit! */
} else
Pwait = t + Slottime; /* wait a bit */
}
}
/* MainLoop()
Main command handling loop.
*/
static void MainLoop(void)
{
char buf[82]; /* input buffer */
char temp; /* temp character storage */
int p; /* position in input buffer */
KEY k; /* keypress */
byte c;
int state; /* current connected state */
int scrollback; /* TRUE if scrolling back */
int i;
/* initalize */
InitKeybuffer();
scrollback = FALSE;
p = 0;
gotoxy(1,24);
state = Connected();
/* main loop */
while(TRUE) {
/* loop until keypress or change in state */
while(!keypressed() && (state == Connected()))
PeriodicHook(); /* do RX, TX, timers */
state = Connected();
/* handle the cursor */
if(Connected()) /* if connected, show cursor */
curon();
else {
if(p) { /* clear any text */
clear_area(24,1,80);
p = 0;
}
curoff();
}
/* handle any user keystrokes */
while(k = Nextkey()) {
/* exit scrollback mode, if appropriate */
if(scrollback && k != UP && k != DOWN
&& k != PGDN && k != PGUP && k != HOME) {
EndScrollback();
scrollback = FALSE;
}
/* ASCII keypress, show on bottom line */
if((c = asciicode(k)) && Connected()) {
bright();
switch(c) {
case '\r': /* send */
buf[p++] = '\n';
buf[p] = '\0';
uputs(BrightAttr, buf);
LinkSend((byte *)buf, p);
clear_area(24,1,80);
p = 0;
break;
case '\b': /* backspace */
if(p>0) {
cprintf("\b \b");
p--;
}
break;
case ' ': /* test for autowrap */
if(AutoWrap && p > AutoWrap) {
i = p-1;
while(i && buf[i] != ' ')
i--;
if(i) { /* wrap word */
buf[i++] = '\n';
temp = buf[i];
buf[i] = '\0';
LinkSend((byte *)buf,i);
uputs(BrightAttr,buf);
buf[i] = temp;
buf[p] = '\0';
strcpy(buf, buf+i);
p -= i;
clear_area(24,1,80);
if(p)
putstring(1,24,80,BrightAttr,buf);
}
}
/* note fall through */
default: /* char */
if(p < 79)
putch(buf[p++] = c);
}
/* else, function key */
} else {
switch(k) {
case DOWN: /* scroll */
if(scrollback) {
if(MoveScrollback(1)) {
EndScrollback();
scrollback = FALSE;
}
}
break;
case UP: /* scroll */
if(!scrollback)
scrollback = StartScrollback();
if(scrollback)
MoveScrollback(-1);
break;
case PGUP: /* scroll */
if(!scrollback)
scrollback = StartScrollback();
if(scrollback)
MoveScrollback(-23);
break;
case PGDN: /* scroll */
if(scrollback) {
if(MoveScrollback(23)) {
EndScrollback();
scrollback = FALSE;
}
}
break;
case HOME: /* scroll */
if(!scrollback)
scrollback = StartScrollback();
if(scrollback)
MoveScrollback(-32000);
break;
case ALTX: /* exit */
if(Connected()) {
sound(400);
delay(200);
nosound();
} else
return;
break;
case ALTB: /* beacon */
SendBeacon();
break;
case ALTC: /* connect */
DoConnect();
break;
case ALTD: /* discnnct */
AX25_Close();
break;
case ALTH: /* help */
DoHelp();
break;
case ALTJ: /* snapshot */
SaveMessage();
DoSnapshot();
RestoreMessage();
state = -1;
break;
case ALTN: /* nodes heard */
DoHeard();
break;
case ALTP: /* pause */
DoPause();
break;
case ALTS: /* node status */
DoStatus();
break;
case ALTU: /* file upload */
SaveMessage();
DoUpload();
RestoreMessage();
state = -1;
break;
case ALTL: /* file download/capture */
SaveMessage();
DoCapture();
RestoreMessage();
state = -1;
break;
case ALTW: /* write scrollback */
SaveMessage();
DoWrite();
RestoreMessage();
state = -1;
break;
case F1:
AddKeystrokes(fkeys[0]);
break;
case F2:
AddKeystrokes(fkeys[1]);
break;
case F3:
AddKeystrokes(fkeys[2]);
break;
case F4:
AddKeystrokes(fkeys[3]);
break;
case F5:
AddKeystrokes(fkeys[4]);
break;
case F6:
AddKeystrokes(fkeys[5]);
break;
case F7:
AddKeystrokes(fkeys[6]);
break;
case F8:
AddKeystrokes(fkeys[7]);
break;
case F9: /* toggle PASSALL */
PassMode = !PassMode;
break;
#ifdef TRACE
case F10: /* debug */
DoDebug();
break;
#endif
}
}
gotoxy(p+1,24);
}
}
}
/* ----- Error handling ----- */
/* OutOfMemory()
Gets called during initialization routines to show an out of
memory error. Prompts for user keystroke and exits.
*/
void OutOfMemory(void)
{
cprintf(" Fatal Error -- Out of Memory! ");
wait();
CRTExit();
exit(-1);
}
/* usage()
Show the command line usage.
*/
static void usage(void)
{
printf("Usage: PMP {-a} {-p<paramfile>}\n");
}
/* ----- Main Program ----- */
void main(int argc, char **argv)
{
int i;
/* process command line parameters */
AutoMode = FALSE;
strcpy(ParamFname, "PMP.CFG");
for(i=1; i<argc; i++) {
if(argv[i][0] != '-') {
usage();
exit(-1);
} else switch(toupper(argv[i][1])) {
case 'A':
AutoMode = TRUE;
break;
case 'P':
strcpy(ParamFname, argv[i]+2);
break;
default:
printf("Unknown switch: %s\n",argv[i]);
usage();
exit(0);
}
}
/* check to see if we've got enough memory to breathe */
if(coreleft() < 65536L) {
printf("Not enough free memory to run PMP.\n");
exit(-1);
}
/* Startup */
InitParameters();
TXKey(FALSE); /* no transmitter */
CRTInit();
TitleScreen();
/* read the parameter file */
gotoxy(1,23);
inverse(); /* set up for errors */
if(ReadParameters()) { /* if error reading parameter file */
wait();
CRTExit();
exit(-1);
}
/* initialize */
RXInit(); /* Initialize RX */
TXQInit(); /* Initialize TX */
AX25_Init(); /* Initialize AX25 LAPB */
HeardInit(); /* Initialize the heard lists */
InitCapture(); /* Initialize capture */
if(!AutoMode)
wait();
time(&StartTime);
normal();
clrscr();
StatusLine();
#ifdef TRACE
LogInit(); /* Initialize the log */
DebugMode = FALSE;
DoDebug();
#endif
#ifdef DEBUG
PassMode = FALSE;
#endif
holdmode = FALSE;
/* crank up timers */
StartBeacon();
SoundEnd = 0;
/* doit! */
MainLoop();
if(Capturing()) /* close capture file */
DoCapture();
/* bye, bye, clean things up... */
CRTExit();
#ifdef TRACE
DumpLog(); /* dump the debug log */
#endif
exit(0);
}